//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include <buola/os/cicontheme.h>

#include <buola/io/cfolder.h>
#include <buola/io/cresourcefinder.h>
#include <buola/utility/cdesktopfile.h>
#include <buola/algorithm/split.h>
#include <buola/algorithm/container.h>
#include <buola/iterator/indirect.h>

namespace buola { namespace os {

std::map<std::string,CIconTheme*> CIconTheme::mThemes;

CIconTheme::SDir::SDir(const std::string &pName)
    :   mName(pName)
    ,   mPathsInit(false)
{
}

void CIconTheme::SDir::InitPaths()
{
    if(mPathsInit) return;
    std::vector<io::CURI> lList=io::icon_finder().FindFolders(mName);

    for(int i=0;i<lList.size();i++)
    {
        SPath &lPath=construct_back(mPaths);

        lPath.mPath=lList[i];

        io::CFolder lFolder(lPath.mPath);
        lPath.mFiles=lFolder.GetChildrenSet();
    }

    mPathsInit=true;
}

bool CIconTheme::SDir::FindIcon(const std::string &pIcon,io::CURI &pURI)
{
    if(!mPathsInit) InitPaths();

    std::string lPNGName=pIcon+".png";
    std::string lSVGName=pIcon+".svg";
    std::string lSVGZName=pIcon+".svgz";

    for(int i=0;i<mPaths.size();i++)
    {
        if(mPaths[i]->mFiles.count(lPNGName))
        {
            pURI=mPaths[i]->mPath/lPNGName;
            return true;
        }
        else if(mPaths[i]->mFiles.count(lSVGName))
        {
            pURI=mPaths[i]->mPath/lSVGName;
            return true;
        }
        else if(mPaths[i]->mFiles.count(lSVGZName))
        {
            pURI=mPaths[i]->mPath/lSVGZName;
            return true;
        }
    }

    return false;
}

bool CIconTheme::SDir::MatchesSize(int pSize)
{
    return(mMinSize<=pSize&&pSize<=mMaxSize);
}

int CIconTheme::SDir::GetDistance(int pSize)
{
    //We can suppose it doesn't match
    return((pSize<mMinSize)?(mMinSize-pSize):(pSize-mMaxSize));
}

CIconTheme::CIconTheme(const std::string &pName)
    :   mName(pName)
{
    io::CURI lURI=io::icon_finder().FindFile(pName+"/index.theme");
    if(lURI.empty()) throw XNotFound("can't find icon theme '"+pName+"' in constructor");

    CDesktopFile lEntry(lURI);

    std::string lDirsString;
    if(!lEntry.Get("Icon Theme","Name",mVisibleName)||
       !lEntry.Get("Icon Theme","Comment",mComment)||
       !lEntry.Get("Icon Theme","Directories",lDirsString))
        throw XData("malformed index.theme");
    mHidden=lEntry.GetValue<bool>("Icon Theme","Hidden",false);
    lEntry.Get("Icon Theme","Example",mExample);

    std::vector<std::string> lDirs;
    split(lDirsString,lDirs,fn::equal_to(','),nDeleteEmpty);

    for(int i=0;i<lDirs.size();i++)
    {
        if(lDirs[i].empty()) continue;
        int lSize=lEntry.GetValue<int>(lDirs[i],"Size",-1);
        if(lSize<0) continue;

        SDir &lDir=construct_back(mDirs,mName+'/'+lDirs[i]);
        lDir.mSize=lSize;

        std::string lType;
        lEntry.Get(lDirs[i],"Type",lType,"Threshold");

        if(lType=="Fixed")
        {
            lDir.mType=TYPE_FIXED;
            lDir.mMaxSize=lDir.mSize;
            lDir.mMinSize=lDir.mSize;
        }
        else if(lType=="Scalable")
        {
            lDir.mType=TYPE_SCALABLE;
            lDir.mMaxSize=lEntry.GetValue<int>(lDirs[i],"MaxSize",lSize);
            lDir.mMinSize=lEntry.GetValue<int>(lDirs[i],"MinSize",lSize);
        }
        else //Threshold
        {
            lDir.mType=TYPE_THRESHOLD;
            int lThreshold=lEntry.GetValue<int>(lDirs[i],"Threshold",2);
            lDir.mMaxSize=lDir.mSize+lThreshold;
            lDir.mMinSize=lDir.mSize-lThreshold;
        }

        std::string lContext;
        lEntry.Get(lDirs[i],"Context",lContext);

        if(lContext=="Actions")
            lDir.mContext=CONTEXT_ACTIONS;
        else if(lContext=="Devices")
            lDir.mContext=CONTEXT_DEVICES;
        else if(lContext=="FileSystems")
            lDir.mContext=CONTEXT_FILESYSTEMS;
        else if(lContext=="MimeTypes")
            lDir.mContext=CONTEXT_MIMETYPES;
        else
            lDir.mContext=CONTEXT_ANY;
    }

    //this goes here so that we don't enter an infinite loop
    mThemes[pName]=this;

    std::string lInherits;
    if(lEntry.Get("Icon Theme","Inherits",lInherits))
    {
        std::vector<std::string> lList;
        split(lInherits,lList,fn::equal_to(','),nDeleteEmpty);
        for(int i=0;i<lList.size();i++)
            Inherit(lList[i]);
    }

    if(pName!="gnome"&&pName!="hicolor")
        Inherit("gnome");
    if(pName!="hicolor")
        Inherit("hicolor");
}

CIconTheme::~CIconTheme()
{

}

void CIconTheme::Inherit(const std::string &pName)
{
    if(pName.empty()||pName==mName) return;
    if(mInheritSet.count(pName)) return;

    CIconTheme *lTheme=Get(pName);
    if(!lTheme) return;

    mInherits.push_back(lTheme);
    mInheritSet.insert(pName);

    for(int i=0;i<lTheme->mInherits.size();i++)
    {
        if(mInheritSet.count(lTheme->mInherits[i]->mName))
            continue;
        mInherits.push_back(lTheme->mInherits[i]);
        mInheritSet.insert(lTheme->mInherits[i]->mName);
    }
}

bool CIconTheme::HasIcon(const std::string &pName,EContext pContext,bool pCheckInherited)
{
    for(auto &d : indirect_range(mDirs))
    {
        if(pContext!=CONTEXT_ANY&&d.mContext!=CONTEXT_ANY&&pContext!=d.mContext) continue;
        io::CURI lDummy;
        if(d.FindIcon(pName,lDummy))
            return true;
    }

    if(pCheckInherited)
    {
        for(int i=0;i<mInherits.size();i++)
        {
            if(mInherits[i]->HasIcon(pName,pContext,false)) return true;
        }
    }

    return false;
}

std::string CIconTheme::GetFirst(const std::vector<std::string> &pNames,EContext pContext,bool pCheckInherited)
{
    for(int i=0;i<pNames.size();i++)
        if(HasIcon(pNames[i],pContext,false))
            return pNames[i];

    if(pCheckInherited)
    {
        for(int i=0;i<mInherits.size();i++)
        {
            std::string lResult=mInherits[i]->GetFirst(pNames,pContext,false);
            if(!lResult.empty())
                return lResult;
        }
    }

    return std::string();
}

io::CURI CIconTheme::GetIcon(const std::string &pName,int &pSize,EContext pContext,bool pCheckInherited)
{
    for(auto i=indirect_begin(mDirs);i!=indirect_end(mDirs);++i)
    {
        if(!i->MatchesSize(pSize)) continue;
        if(pContext!=CONTEXT_ANY&&i->mContext!=CONTEXT_ANY&&pContext!=i->mContext) continue;

        io::CURI lURI;
        if(i->FindIcon(pName,lURI))
            return lURI;
    }

    int lMinDist=-1;
    int lMinRealSize=0;
    io::CURI lMinURI;

    for(auto i=indirect_begin(mDirs);i!=indirect_end(mDirs);++i)
    {
        if(i->MatchesSize(pSize)) continue;
        if(pContext!=CONTEXT_ANY&&i->mContext!=CONTEXT_ANY&&pContext!=i->mContext) continue;
        int lDist=i->GetDistance(pSize);

        if(lMinDist>=0&&lDist>=lMinDist) continue;

        io::CURI lURI;
        if(i->FindIcon(pName,lURI))
        {
            if(pSize<i->mMinSize)
                lMinRealSize=i->mMinSize;
            else if(pSize>i->mMaxSize)
                lMinRealSize=i->mMaxSize;
            else
                lMinRealSize=pSize;

            lMinDist=lDist;
            lMinURI=lURI;
        }
    }

    if(lMinDist>=0)
    {
        pSize=lMinRealSize;
        return lMinURI;
    }

    if(pCheckInherited)
    {
        for(int i=0;i<mInherits.size();i++)
        {
            try
            {
                return mInherits[i]->GetIcon(pName,pSize,pContext,false);
            } catch(XNotFound) {}
        }
    }

    throw XNotFound(I18N("icon not found"));
}

CIconTheme *CIconTheme::Get(const std::string &pName)
{
    auto i=mThemes.find(pName);
    if(i!=mThemes.end())
        return i->second;

    try
    {
        return new CIconTheme(pName);
    }
    catch(XNotFound)
    {
        mThemes[pName]=nullptr;
        return nullptr;
    }
}

/*namespace os*/ } /*namespace buola*/ }
